home *** CD-ROM | disk | FTP | other *** search
- /* TrackEffectGenerator.c */
- /*****************************************************************************/
- /* */
- /* Out Of Phase: Digital Music Synthesis on General Purpose Computers */
- /* Copyright (C) 1994 Thomas R. Lawrence */
- /* */
- /* This program is free software; you can redistribute it and/or modify */
- /* it under the terms of the GNU General Public License as published by */
- /* the Free Software Foundation; either version 2 of the License, or */
- /* (at your option) any later version. */
- /* */
- /* This program is distributed in the hope that it will be useful, */
- /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
- /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
- /* GNU General Public License for more details. */
- /* */
- /* You should have received a copy of the GNU General Public License */
- /* along with this program; if not, write to the Free Software */
- /* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
- /* */
- /* Thomas R. Lawrence can be reached at tomlaw@world.std.com. */
- /* */
- /*****************************************************************************/
-
- #include "MiscInfo.h"
- #include "Audit.h"
- #include "Debug.h"
- #include "Definitions.h"
-
- #define ShowMe_NoteObjectRec
- #include "TrackEffectGenerator.h"
- #include "EffectSpecList.h"
- #include "Memory.h"
- #include "DelayLine.h"
- #include "BinaryCodedDecimal.h"
- #include "LinearTransition.h"
- #include "NoteObject.h"
- #include "IncrementalParameterUpdator.h"
- #include "NLProc.h"
- #include "FilterArray.h"
- #include "Analyzer.h"
-
-
- typedef struct CommandConsCell
- {
- /* link. only forward link used since insertion sorting is not necessary */
- struct CommandConsCell* Next;
-
- /* command that has been suspended */
- NoteObjectRec* Command;
-
- /* this is the start time of the note, used for ordering the scanning gap list */
- long StartTime;
- } CommandConsCell;
-
-
- typedef struct OneEffectRec
- {
- struct OneEffectRec* Next;
- EffectTypes Type;
- union
- {
- DelayLineRec* DelayEffect;
- NLProcRec* NLProcEffect;
- FilterArrayRec* FilterEffect;
- AnalyzerRec* AnalyzerEffect;
- } u;
- } OneEffectRec;
-
-
- struct TrackEffectGenRec
- {
- LargeBCDType CurrentAccent1;
- struct LinearTransRec* Accent1Change;
- long Accent1ChangeCountdown;
-
- LargeBCDType CurrentAccent2;
- struct LinearTransRec* Accent2Change;
- long Accent2ChangeCountdown;
-
- LargeBCDType CurrentAccent3;
- struct LinearTransRec* Accent3Change;
- long Accent3ChangeCountdown;
-
- LargeBCDType CurrentAccent4;
- struct LinearTransRec* Accent4Change;
- long Accent4ChangeCountdown;
-
- OneEffectRec* List;
- MyBoolean Stereo;
-
- /* list of commands that have been scheduled but haven't been executed yet. */
- CommandConsCell* ScanningGapListHead;
- CommandConsCell* ScanningGapListTail;
- /* this is the current duration update index for the scanning gap list */
- long ExecutionIndex;
-
- /* track effect enable. turns off processing, but command handling */
- /* remains enabled (otherwise you couldn't turn processing back on) */
- MyBoolean Enable;
- };
-
-
- static CommandConsCell* CommandConsFreeList = NIL;
-
-
- /* dispose of cached effect generator structures */
- void FlushTrackEffectGeneratorInfo(void)
- {
- while (CommandConsFreeList != NIL)
- {
- CommandConsCell* Temp;
-
- Temp = CommandConsFreeList;
- CommandConsFreeList = CommandConsFreeList->Next;
- ReleasePtr((char*)Temp);
- }
- }
-
-
- /* create a new track effect generator */
- TrackEffectGenRec* NewTrackEffectGenerator(struct EffectSpecListRec* SpecList,
- long FramesPerSecond, MyBoolean DoingStereo,
- float InverseVolume, struct MainWindowRec* MainWindow,
- long ScanningGapWidthInEnvelopeTicks)
- {
- TrackEffectGenRec* Generator;
- OneEffectRec* Appender;
- long List;
- long Scan;
-
- CheckPtrExistence(SpecList);
- Generator = (TrackEffectGenRec*)AllocPtrCanFail(sizeof(TrackEffectGenRec),
- "TrackEffectGenRec");
- if (Generator == NIL)
- {
- FailurePoint1:
- return NIL;
- }
- Generator->List = NIL;
- Generator->Stereo = DoingStereo;
- Generator->ScanningGapListHead = NIL;
- Generator->ScanningGapListTail = NIL;
- Generator->Enable = True;
-
- /* this is the current envelope update index for removing things from the */
- /* scanning gap list (i.e. the back edge of the scanning gap) */
- /* by setting this negative, we cause the scanning gap to open. */
- Generator->ExecutionIndex = - ScanningGapWidthInEnvelopeTicks;
-
- /* initialize accent trackers */
- Generator->CurrentAccent1 = 0;
- Generator->Accent1ChangeCountdown = 0;
- Generator->Accent1Change = NewLinearTransition(0,0,1);
- if (Generator->Accent1Change == NIL)
- {
- FailurePoint2:
- ReleasePtr((char*)Generator);
- goto FailurePoint1;
- }
- Generator->CurrentAccent2 = 0;
- Generator->Accent2ChangeCountdown = 0;
- Generator->Accent2Change = NewLinearTransition(0,0,1);
- if (Generator->Accent2Change == NIL)
- {
- FailurePoint3:
- DisposeLinearTransition(Generator->Accent1Change);
- goto FailurePoint2;
- }
- Generator->CurrentAccent3 = 0;
- Generator->Accent3ChangeCountdown = 0;
- Generator->Accent3Change = NewLinearTransition(0,0,1);
- if (Generator->Accent3Change == NIL)
- {
- FailurePoint4:
- DisposeLinearTransition(Generator->Accent2Change);
- goto FailurePoint3;
- }
- Generator->CurrentAccent4 = 0;
- Generator->Accent4ChangeCountdown = 0;
- Generator->Accent4Change = NewLinearTransition(0,0,1);
- if (Generator->Accent4Change == NIL)
- {
- FailurePoint5:
- DisposeLinearTransition(Generator->Accent3Change);
- goto FailurePoint4;
- }
-
- /* build list of thingers */
- Appender = NIL;
- List = GetEffectSpecListLength(SpecList);
- for (Scan = 0; Scan < List; Scan += 1)
- {
- OneEffectRec* Effect;
-
- /* allocate record */
- Effect = (OneEffectRec*)AllocPtrCanFail(sizeof(OneEffectRec),"OneEffectRec");
- if (Effect == NIL)
- {
- FailurePoint6:
- while (Generator->List != NIL)
- {
- Effect = Generator->List;
- Generator->List = Generator->List->Next;
- switch (Effect->Type)
- {
- default:
- EXECUTE(PRERR(ForceAbort,"NewTrackEffectGenerator: bad effect type"));
- break;
- case eDelayEffect:
- DisposeDelayLineProcessor(Effect->u.DelayEffect);
- break;
- case eNLProcEffect:
- DisposeNLProcProcessor(Effect->u.NLProcEffect);
- break;
- case eFilterEffect:
- DisposeFilterArrayProcessor(Effect->u.FilterEffect);
- break;
- case eAnalyzerEffect:
- DisposeAnalyzer(Effect->u.AnalyzerEffect);
- break;
- }
- ReleasePtr((char*)Effect);
- }
- DisposeLinearTransition(Generator->Accent4Change);
- goto FailurePoint5;
- }
- /* fill in fields */
- Effect->Type = GetEffectSpecListElementType(SpecList,Scan);
- switch (Effect->Type)
- {
- default:
- EXECUTE(PRERR(ForceAbort,"NewTrackEffectGenerator: bad effect type"));
- break;
- case eDelayEffect:
- Effect->u.DelayEffect = NewDelayLineProcessor(
- GetDelayEffectFromEffectSpecList(SpecList,Scan),FramesPerSecond);
- if (Effect->u.DelayEffect == NIL)
- {
- FailurePoint6b:
- ReleasePtr((char*)Effect);
- goto FailurePoint6;
- }
- break;
- case eNLProcEffect:
- Effect->u.NLProcEffect = NewNLProcProcessor(
- GetNLProcEffectFromEffectSpecList(SpecList,Scan),DoingStereo,
- InverseVolume);
- if (Effect->u.NLProcEffect == NIL)
- {
- goto FailurePoint6b;
- }
- break;
- case eFilterEffect:
- Effect->u.FilterEffect = NewFilterArrayProcessor(
- GetFilterEffectFromEffectSpecList(SpecList,Scan),FramesPerSecond,
- DoingStereo);
- if (Effect->u.FilterEffect == NIL)
- {
- goto FailurePoint6b;
- }
- break;
- case eAnalyzerEffect:
- Effect->u.AnalyzerEffect = NewAnalyzer(MainWindow,
- GetAnalyzerEffectFromEffectSpecList(SpecList,Scan),DoingStereo,
- InverseVolume);
- if (Effect->u.AnalyzerEffect == NIL)
- {
- goto FailurePoint6b;
- }
- break;
- }
- /* link */
- Effect->Next = NIL;
- if (Appender == NIL)
- {
- Generator->List = Effect;
- }
- else
- {
- Appender->Next = Effect;
- }
- Appender = Effect;
- }
- return Generator;
- }
-
-
- /* dispose of a track effect generator */
- void DisposeTrackEffectGenerator(TrackEffectGenRec* Generator)
- {
- CommandConsCell* Scan;
-
- CheckPtrExistence(Generator);
- Scan = Generator->ScanningGapListHead;
- while (Scan != NIL)
- {
- CommandConsCell* Temp;
-
- Temp = Scan;
- Scan = Scan->Next;
- Temp->Next = CommandConsFreeList;
- CommandConsFreeList = Temp;
- }
- DisposeLinearTransition(Generator->Accent1Change);
- DisposeLinearTransition(Generator->Accent2Change);
- DisposeLinearTransition(Generator->Accent3Change);
- DisposeLinearTransition(Generator->Accent4Change);
- while (Generator->List != NIL)
- {
- OneEffectRec* Temp;
-
- Temp = Generator->List;
- Generator->List = Generator->List->Next;
- switch (Temp->Type)
- {
- default:
- EXECUTE(PRERR(ForceAbort,"DisposeTrackEffectGenerator: bad effect type"));
- break;
- case eDelayEffect:
- DisposeDelayLineProcessor(Temp->u.DelayEffect);
- break;
- case eNLProcEffect:
- DisposeNLProcProcessor(Temp->u.NLProcEffect);
- break;
- case eFilterEffect:
- DisposeFilterArrayProcessor(Temp->u.FilterEffect);
- break;
- case eAnalyzerEffect:
- DisposeAnalyzer(Temp->u.AnalyzerEffect);
- break;
- }
- ReleasePtr((char*)Temp);
- }
- ReleasePtr((char*)Generator);
- }
-
-
- /* generate effect cycle */
- void ApplyTrackEffectGenerator(TrackEffectGenRec* Generator,
- largefixedsigned* Data, long NumFrames)
- {
- OneEffectRec* Scan;
-
- CheckPtrExistence(Generator);
- Scan = Generator->List;
- if ((Scan != NIL) && Generator->Enable)
- {
- float Accent1;
- float Accent2;
- float Accent3;
- float Accent4;
-
- Accent1 = LargeBCD2Single(Generator->CurrentAccent1);
- Accent2 = LargeBCD2Single(Generator->CurrentAccent2);
- Accent3 = LargeBCD2Single(Generator->CurrentAccent3);
- Accent4 = LargeBCD2Single(Generator->CurrentAccent4);
- while (Scan != NIL)
- {
- switch (Scan->Type)
- {
- default:
- EXECUTE(PRERR(ForceAbort,"ApplyTrackEffectGenerator: bad effect type"));
- break;
- case eDelayEffect:
- UpdateDelayLineState(Scan->u.DelayEffect,Accent1,Accent2,Accent3,Accent4);
- if (Generator->Stereo)
- {
- ApplyDelayLineStereo(Data,NumFrames,Scan->u.DelayEffect);
- }
- else
- {
- ApplyDelayLineMono(Data,NumFrames,Scan->u.DelayEffect);
- }
- break;
- case eNLProcEffect:
- UpdateNLProcState(Scan->u.NLProcEffect,Accent1,Accent2,Accent3,Accent4);
- ApplyNLProc(Data,NumFrames,Scan->u.NLProcEffect);
- break;
- case eFilterEffect:
- UpdateFilterArrayState(Scan->u.FilterEffect,Accent1,Accent2,Accent3,Accent4);
- ApplyFilterArray(Data,NumFrames,Scan->u.FilterEffect);
- break;
- case eAnalyzerEffect:
- ApplyAnalyzer(Data,NumFrames,Scan->u.AnalyzerEffect);
- break;
- }
- Scan = Scan->Next;
- }
- }
- }
-
-
- /* hand off command to be handled by effect generator. the command will be scheduled */
- /* to occur at the time ScanningGapFrontInEnvelopeTicks (which will be */
- /* ScanningGapWidthInEnvelopeTicks in the future from now). */
- MyBoolean TrackEffectHandleCommand(TrackEffectGenRec* Generator,
- struct NoteObjectRec* Command,
- long ScanningGapFrontInEnvelopeTicks)
- {
- CheckPtrExistence(Generator);
- CheckPtrExistence(Command);
- /* don't bother unless there are effects to process */
- if (Generator->List != NIL)
- {
- CommandConsCell* Cell;
-
- if (CommandConsFreeList != NIL)
- {
- Cell = CommandConsFreeList;
- CommandConsFreeList = CommandConsFreeList->Next;
- }
- else
- {
- Cell = (CommandConsCell*)AllocPtrCanFail(sizeof(CommandConsCell),
- "CommandConsCell");
- if (Cell == NIL)
- {
- return False;
- }
- }
-
- /* fill in data fields */
- Cell->Command = Command;
- Cell->StartTime = ScanningGapFrontInEnvelopeTicks;
-
- /* insert into list */
- /* there is no need to sort, since the start time of commands can't */
- /* be adjusted. just append to the list */
- Cell->Next = NIL;
- if (Generator->ScanningGapListTail == NIL)
- {
- Generator->ScanningGapListHead = Cell;
- }
- else
- {
- Generator->ScanningGapListTail->Next = Cell;
- }
- Generator->ScanningGapListTail = Cell;
- }
-
- return True;
- }
-
-
- /* increment duration timer. this is called once per envelope tick to adjust */
- /* all of the parameter transition tracking devices */
- void TrackEffectIncrementDurationTimer(TrackEffectGenRec* Generator,
- long NumDurationTicks)
- {
- CheckPtrExistence(Generator);
- /* don't bother unless there are effects to process */
- if ((Generator->List != NIL) && Generator->Enable)
- {
- UpdateOne(&Generator->CurrentAccent1,Generator->Accent1Change,
- &Generator->Accent1ChangeCountdown,NumDurationTicks);
- UpdateOne(&Generator->CurrentAccent2,Generator->Accent2Change,
- &Generator->Accent2ChangeCountdown,NumDurationTicks);
- UpdateOne(&Generator->CurrentAccent3,Generator->Accent3Change,
- &Generator->Accent3ChangeCountdown,NumDurationTicks);
- UpdateOne(&Generator->CurrentAccent4,Generator->Accent4Change,
- &Generator->Accent4ChangeCountdown,NumDurationTicks);
- }
- }
-
-
- /* process commands in the queue that occur now. this should be called after */
- /* queueing commands, but before incrementing the execution index, and before */
- /* processing the data, so that commands are handled at the beginning of a transition. */
- void TrackEffectProcessQueuedCommands(TrackEffectGenRec* Generator)
- {
- CheckPtrExistence(Generator);
- if (Generator->List != NIL)
- {
- while ((Generator->ScanningGapListHead != NIL)
- && (Generator->ScanningGapListHead->StartTime <= Generator->ExecutionIndex))
- {
- CommandConsCell* Cell;
-
- /* since the start time of commands can't be adjusted, there should */
- /* never be a command that is strictly less than. */
- ERROR(Generator->ScanningGapListHead->StartTime < Generator->ExecutionIndex,
- PRERR(AllowResume,"TrackEffectProcessQueuedCommands: early command"));
- /* unlink the cons cell */
- Cell = Generator->ScanningGapListHead;
- Generator->ScanningGapListHead = Generator->ScanningGapListHead->Next;
- if (Generator->ScanningGapListHead == NIL)
- {
- Generator->ScanningGapListTail = NIL;
- }
- /* see what we're supposed to do with the command */
- switch (Cell->Command->Flags & ~eCommandFlag)
- {
- default:
- EXECUTE(PRERR(ForceAbort,
- "TrackEffectProcessQueuedCommands: bad command"));
- break;
- case eCmdSetEffectParam1: /* specify the new default effect parameter in <1l> */
- case eCmdSetScoreEffectParam1:
- Generator->CurrentAccent1 = Cell->Command->a.Command.Argument1;
- Generator->Accent1ChangeCountdown = 0;
- break;
- case eCmdIncEffectParam1: /* add <1l> to the default effect parameter */
- case eCmdIncScoreEffectParam1:
- Generator->CurrentAccent1 += Cell->Command->a.Command.Argument1;
- Generator->Accent1ChangeCountdown = 0;
- break;
- case eCmdSweepEffectParamAbs1: /* <1l> = new effect parameter, <2xs> = # of beats to get there */
- case eCmdSweepScoreEffectParamAbs1:
- SweepToNewValue(Generator->CurrentAccent1,Generator->Accent1Change,
- &Generator->Accent1ChangeCountdown,Cell->Command->a.Command.Argument1,
- Cell->Command->a.Command.Argument2);
- break;
- case eCmdSweepEffectParamRel1: /* <1l> = effect parameter adjust, <2xs> = # of beats to get there */
- case eCmdSweepScoreEffectParamRel1:
- SweepToAdjustedValue(Generator->CurrentAccent1,Generator->Accent1Change,
- &Generator->Accent1ChangeCountdown,Cell->Command->a.Command.Argument1,
- Cell->Command->a.Command.Argument2);
- break;
-
- case eCmdSetEffectParam2: /* specify the new default effect parameter in <1l> */
- case eCmdSetScoreEffectParam2:
- Generator->CurrentAccent2 = Cell->Command->a.Command.Argument1;
- Generator->Accent2ChangeCountdown = 0;
- break;
- case eCmdIncEffectParam2: /* add <1l> to the default effect parameter */
- case eCmdIncScoreEffectParam2:
- Generator->CurrentAccent2 += Cell->Command->a.Command.Argument1;
- Generator->Accent2ChangeCountdown = 0;
- break;
- case eCmdSweepEffectParamAbs2: /* <1l> = new effect parameter, <2xs> = # of beats to get there */
- case eCmdSweepScoreEffectParamAbs2:
- SweepToNewValue(Generator->CurrentAccent2,Generator->Accent2Change,
- &Generator->Accent2ChangeCountdown,Cell->Command->a.Command.Argument1,
- Cell->Command->a.Command.Argument2);
- break;
- case eCmdSweepEffectParamRel2: /* <1l> = effect parameter adjust, <2xs> = # of beats to get there */
- case eCmdSweepScoreEffectParamRel2:
- SweepToAdjustedValue(Generator->CurrentAccent2,Generator->Accent2Change,
- &Generator->Accent2ChangeCountdown,Cell->Command->a.Command.Argument1,
- Cell->Command->a.Command.Argument2);
- break;
-
- case eCmdSetEffectParam3: /* specify the new default effect parameter in <1l> */
- case eCmdSetScoreEffectParam3:
- Generator->CurrentAccent3 = Cell->Command->a.Command.Argument1;
- Generator->Accent3ChangeCountdown = 0;
- break;
- case eCmdIncEffectParam3: /* add <1l> to the default effect parameter */
- case eCmdIncScoreEffectParam3:
- Generator->CurrentAccent3 += Cell->Command->a.Command.Argument1;
- Generator->Accent3ChangeCountdown = 0;
- break;
- case eCmdSweepEffectParamAbs3: /* <1l> = new effect parameter, <2xs> = # of beats to get there */
- case eCmdSweepScoreEffectParamAbs3:
- SweepToNewValue(Generator->CurrentAccent3,Generator->Accent3Change,
- &Generator->Accent3ChangeCountdown,Cell->Command->a.Command.Argument1,
- Cell->Command->a.Command.Argument2);
- break;
- case eCmdSweepEffectParamRel3: /* <1l> = effect parameter adjust, <2xs> = # of beats to get there */
- case eCmdSweepScoreEffectParamRel3:
- SweepToAdjustedValue(Generator->CurrentAccent3,Generator->Accent3Change,
- &Generator->Accent3ChangeCountdown,Cell->Command->a.Command.Argument1,
- Cell->Command->a.Command.Argument2);
- break;
-
- case eCmdSetEffectParam4: /* specify the new default effect parameter in <1l> */
- case eCmdSetScoreEffectParam4:
- Generator->CurrentAccent4 = Cell->Command->a.Command.Argument1;
- Generator->Accent4ChangeCountdown = 0;
- break;
- case eCmdIncEffectParam4: /* add <1l> to the default effect parameter */
- case eCmdIncScoreEffectParam4:
- Generator->CurrentAccent4 += Cell->Command->a.Command.Argument1;
- Generator->Accent4ChangeCountdown = 0;
- break;
- case eCmdSweepEffectParamAbs4: /* <1l> = new effect parameter, <2xs> = # of beats to get there */
- case eCmdSweepScoreEffectParamAbs4:
- SweepToNewValue(Generator->CurrentAccent4,Generator->Accent4Change,
- &Generator->Accent4ChangeCountdown,Cell->Command->a.Command.Argument1,
- Cell->Command->a.Command.Argument2);
- break;
- case eCmdSweepEffectParamRel4: /* <1l> = effect parameter adjust, <2xs> = # of beats to get there */
- case eCmdSweepScoreEffectParamRel4:
- SweepToAdjustedValue(Generator->CurrentAccent4,Generator->Accent4Change,
- &Generator->Accent4ChangeCountdown,Cell->Command->a.Command.Argument1,
- Cell->Command->a.Command.Argument2);
- break;
- case eCmdTrackEffectEnable:
- Generator->Enable = (Cell->Command->a.Command.Argument1 < 0);
- break;
- }
- /* dispose */
- Cell->Next = CommandConsFreeList;
- CommandConsFreeList = Cell;
- }
-
- /* increment our scanning gap back edge clock, after scheduling commands */
- /* (this way, commands are scheduled on the beginning of the clock they */
- /* should occur on). */
- Generator->ExecutionIndex += 1;
- /* since this routine is only called when samples are being generated, */
- /* we don't have to worry about when to increment this counter */
- }
- }
-